/* Copyright (C) 2010  Romolo Camplani, Marco Fortunato, Marco Marelli, Guido Rota, Fabio A. Schreiber et al.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

package org.dei.perla.sys.hlqexecutor;

import org.dei.perla.parser.util.Duration;


public class Query {
	
	private boolean timeBased = false;
	private boolean eventBased = false;
	private Duration duration = null;
	private int sample = 0;
	private String nameTableEvent = "";
	private String nameView = "";
	private String createView = "CREATE VIEW " + nameView + "AS ";
	private String sql = "";
	private String sqlSelect = " SELECT ";
	private String sqlFrom = " FROM ";
	private String sqlWhere = " WHERE ";
	private String sqlGroupBy = " GROUP BY ";
	private String sqlHaving = " HAVING ";
	private boolean unionAll = false;
	private boolean distinct = false;
	
	public Query() {
		
	}
	
	public void setNameView(String nameView) {
		this.nameView += nameView;
	}
	
	public void setSelect(String s) {
		this.sqlSelect += s;
	}
	
	public void  setFrom(String s) {
		this.sqlFrom += s;
	}
	
	public boolean setWhere(String s) {
		return true;
	}
	
	public boolean setGroupBy(String s) {
		return true;
	}
	
	public boolean setHaving(String s) {
		return true;
	}
	
	public String getQuerySQL() throws QueryException{
		
		//la creazione di una vista è opzionale e puo servire come struttura d'appoggio   
		if(!nameView.replaceAll(" ","").equals(""))
			sql += createView;
		
		//clausola necessaria per una query
		if(this.sqlSelect.replaceAll(" ", "").equalsIgnoreCase("SELECT")) throw new QueryException("Query not valid! Set SELECT field list.");
		else sql += sqlSelect; 
		
		//clausola necessaria per una query
		if(this.sqlFrom.replaceAll(" ", "").equalsIgnoreCase("FROM")) throw new QueryException("Query not valid! Set FROM field list.");
		else sql += sqlFrom;
		
		//clausola opzionale per una query
		if(!this.sqlWhere.replaceAll(" ", "").equalsIgnoreCase("WHERE"))	sql += sqlWhere;
		
		//clausola opzionale per una query
		if(!this.sqlGroupBy.replaceAll(" ", "").equalsIgnoreCase("GROUPBY"))
			if(!this.sqlHaving.replaceAll(" ","").equalsIgnoreCase("HAVING")) sql += sqlGroupBy + sqlHaving;
			else sql += sqlGroupBy;
		return sql;
	}
	
	/**
	 * Permette di sapere se si tratta di una query event based
	 * @return un valore boolean
	 */
	public boolean isEventBased() {
		return this.eventBased;
	}
	
	/**
	 * Permette di sapere se si tratta di una query time based
	 * @return un valore boolean
	 */
	public boolean isTimeBased() {
		return this.timeBased;
	}
	
	/**
	 * Permette di sapere se la query è vuota. Una query è definita vuota se la clausola Select non è 
	 * definita e/o la clausola From non è definita. 
	 * @return un valore boolean
	 */
	public boolean isEmpty() {
		return this.sqlSelect.replaceAll(" ", "").equalsIgnoreCase("SELECT") ||
			this.sqlFrom.replaceAll(" ", "").equalsIgnoreCase("FROM");
	}

	/**
	 * Setta la durata della query se si tratta di una query time based.
	 * @param duration un oggetto Duration rappresentante la durata della query.
	 */
	public void setDuration(Duration duration) {
		this.duration = duration;
		this.timeBased = true;
	}

	/**
	 * Restituisce la durata della query se si tratta di una query time based
	 * altrimenti restituisce null.
	 * @return un oggetto Duration
	 */
	public Duration getDuration() {
		return duration;
	}

	/**
	 * Setta la durata della query se si tratta di una query event based.
	 * @param sample un int rappresentante il numero di campioni, ovviamente
	 * deve essere un intero maggiore di 0.
	 */
	public void setSample(int sample) {
		if (sample > 0) {
			this.sample = sample;
			this.eventBased = true;
		}
			
	}

	/**
	 * Restituisce il numero di campioni per che fanno scattare l'evento della query 
	 * se si tratta di una query event based altrimenti restituisce 0.
	 * @return the sample
	 */
	public int getSample() {
		return sample;
	}

	/**
	 * Setta il nome della tabella (o vista ) su cui impostare un trigger per monitorare 
	 * l'evento.
	 * @param nameTableEvent un oggetto String rappresentante il nome della tabella
	 */
	public void setNameTableEvent(String nameTableEvent) {
		this.nameTableEvent = nameTableEvent;
	}

	/**
	 * Restituisce il nome della tabella da monitorare in attesa che si verifichi l'evento.
	 * Restituisce null se non si tratta di una query event based.
	 * @return the nameTableEvent
	 */
	public String getNameTableEvent() {
		return nameTableEvent;
	}

	/**
	 * Crea una copia esatta dell'oggetto
	 */
	public Query clone() {
		Query tmp = new Query();
		// TODO bisogna settare tutti i valori per creare la copia
		return tmp;
	}
	
	/**
	 * Confranta l'oggetto query this con un altro oggetto query e restituisce 
	 * true nel caso in cui i due oggetti sono uguali false altrimenti. Due oggetti 
	 * sono uguali se tutte le informazioni contenute al proprio interno sono le stesse.
	 * Se le stringhe sql sono entrambe vuote vengono intese uguali.
	 * @param otherObject un oggetto Query da confrontare
	 * @return un boolean
	 */
	@Override
	public boolean equals(Object otherObject) {
		
		
		if (this == otherObject)
			return true;
		
		if (this == null)
			return false;
		
		if (getClass() != otherObject.getClass())
			return false;
		
		Query other = (Query) otherObject; 
		
		try {
			if(!this.isEmpty() && !other.isEmpty()) {
				if(!this.getQuerySQL().equalsIgnoreCase(other.getQuerySQL()))
					return false;
			}
			else if(!this.isEmpty() && other.isEmpty())
					return false;
				
		} catch (QueryException e) {
			e.printStackTrace();
		}
		
		if(this.isTimeBased() != other.isTimeBased())
			return false;
		
		if(this.isEventBased() != other.isTimeBased())
			return false;
	
		if(this.duration != null && other.getDuration() != null) {
			if(!this.duration.equals(other.getDuration()))
				return false;
		}
		else if (this.duration == null && other.getDuration() != null)
			return false;

		
		if(this.getSample() != other.getSample())
			return false;
		
		if(!this.getNameTableEvent().equalsIgnoreCase(other.getNameTableEvent()))
			return false;
		
		return true;

	}

	/**
	 * Setta l'oggetto unionAll che di default è false.
	 * @param unionAll 
	 */
	public void setUnionAll(boolean unionAll) {
		this.unionAll = unionAll;
	}

	/**
	 * Permette di sapere se la query è in union.
	 * @return un boolean
	 */
	public boolean isUnionAll() {
		return unionAll;
	}

	/**
	 * Setta l'attributo distinct di una query (di default false).
	 * L'attributo indica se la select deve restituire, in caso di piu' record uguali, una sola occorrenza. 
	 * @param distinct 
	 */
	public void setDistinct(boolean distinct) {
		this.distinct = distinct;
	}

	/**
	 * Ritorna informazioni riguardo al fatto che una select di una query deve 
	 * restituire ogni occorrenza di ogni record (anche se ripetute) nel caso false,
	 * mentre nel caso true non sono presenti record uguali.
	 * @return un oggeto boolean
	 */
	public boolean isDistinct() {
		return distinct;
	}
	
}
